/* Copyright (C) 2007-2010 STS Soft

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

using System;
using System.Collections.Generic;

namespace STSdb.Data
{
    public interface ITransaction
    {
        IEnumerable<JournalItem> Journal();
        void Commit();
        void Rollback();
    }

    public class JournalItem
    {
        public ulong Handle { get; set; }
        public ulong Offset { get; set; }
        public byte[] Data { get; set; }

        public JournalItem()
        {
        }

        public JournalItem(ulong handle, ulong offset, byte[] data)
        {
            Handle = handle;
            Offset = offset;
            Data = data;
        }
    }

    public class Transaction
    {
        public readonly StorageEngine Engine;
        public ITransaction[] TransactionObjects { get; private set; }

        public Transaction(StorageEngine engine, params ITransaction[] transactionObjects)
        {
            if (engine == null)
                throw new ArgumentNullException("engine");
            if (transactionObjects == null)
                throw new ArgumentNullException();

            Engine = engine;
            TransactionObjects = transactionObjects;
        }

        #region ITransaction Members

        public void Commit()
        {
            XTable<ulong, JournalItem> journal;
            lock (Engine.JournalScheme.SyncRoot)
                journal = new XTable<ulong, JournalItem>(Engine.JournalScheme.RawFile, Engine.JournalScheme.RawFile.Length);

            ulong index = 0;
            foreach (ITransaction transactionObject in TransactionObjects)
            {
                foreach (var journalItem in transactionObject.Journal())
                {
                    journal[index] = journalItem;

                    index++;
                }
            }
            journal.Commit();

            ulong id;
            lock (Engine.JournalScheme.SyncRoot)
            {
                var row = Engine.JournalScheme.LastRow;
                id = (row == null) ? 0 : row.Key + 1;

                Engine.JournalScheme[id] = journal.Offset;
                Engine.JournalScheme.Commit();
            }

            foreach (ITransaction transactionObject in TransactionObjects)
                transactionObject.Commit();

            lock (Engine.JournalScheme.SyncRoot)
            {
                Engine.JournalScheme.Delete(id);
                Engine.JournalScheme.Commit();
            }
        }

        public void Rollback()
        {
            foreach (ITransaction transactionObject in TransactionObjects)
                transactionObject.Rollback();
        }

        #endregion
    }
}
